Repository navigation
Conversation
Collaborator
Author
|
Status Reproduction on Bun 1.4.3 (Linux x64): mkdir t && cd t
echo 'import { test } from "bun:test"; test("s", () => {});' > s.test.js
ln -s . x && ln -s . y
bun test # never exits; memory and open fds growWith this branch the same tree prints This PR is a draft, stacked on #42921. One item is open: the question for a maintainer at the top of Notes. The cost numbers are in the body. |
Collaborator
Author
|
Updated 9:58 AM PT - Oct 15th, 2026
⏳ @robobun, your commit 1ec4bee is still building in |
robobun
force-pushed
the
robobun/d068ecf6/test-scanner-symlinks
branch
from
October 15, 2026 13:23
46db8f9 to
a0ae1aa
Compare
robobun
changed the base branch from
main
to
robobun/730a1a7f/test-scanner-cached-dirs
October 15, 2026 13:23
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
bun testnever exits when two directory symlinks lead back up the tree (ln -s . x && ln -s . y). Memory grows without a bound: 363 MB after 6 s.Scanner::next(src/runtime/cli/test/Scanner.rs) queues a symlinked directory like a real one, so the walk lists a directory once per path to it.Fix
Entry::link(src/resolver/fs.rs) returns the kind, a link bit, and the real pathRealFS::kindalready reads.bun test latest/,latest -> packages/v2) matches nothing.bun test ./latestworks.directory linkstests intest/cli/test/bun-test.test.ts. Bun 1.4.3 fails 17.Background
bun testscans every directory #42921: the resolver replays a cached listing there.Scanner::nextper entry.RealFS::kindstats an entry on first use and stores a symlink's real path.O_NOFOLLOW: a test behind a link stops running. Considered a visited (device, inode) set (bun test: report directories the scanner cannot read, scan each directory once #41465): readdir order decides which path records a file.Downsides
Bun.FileSystemRouterhas the same walk (src/router/lib.rs). Two aliasing path roots still record a file twice (bun test: list a test file once when two path arguments select it #43361).Notes
Question for a maintainer. The walk records a directory that only a link leads to under its real path. A substring filter or a file ignore pattern that names only the link path stops matching. Is that acceptable? If not, a filter pass that runs after the walk, only on trees with links, can keep both names (about 55 more lines). The other way, one listing per link path, brings back the growth this PR removes.
Stacking. The base branch is #42921 (
robobun/730a1a7f/test-scanner-cached-dirs). An earlier version of this PR had its own replay of a cached listing for a followed directory (a link to a parent of the cwd needs it). #42921 does that in the resolver for every directory, so the copy is gone. If #42921 does not merge, this PR needs that replay back.The rule, in the directory arm of
Scanner::next, after the existing prunes.Cost, measured. Release builds of the base (#42921 at ca43827) and of this PR (1ec4bee), built in the same directory with the same toolchain, linux x64. Counts come from gdb:
catch syscallwith ignore counts, breakpoints on the mimalloc entry points, andstepithrough one call.Scanner::next, directory entry: 102 instructions (base 95). The 7 are the test of the link bit and the test of "this directory was entered through a link"./usr/lib/llvm-23, an installed Debian package: 443 listings for 443 directories. The base never ends there, becausebuild/Release -> ..andbuild/Debug+Asserts -> ..both lead to the parent.size: 105,075,871 to 105,083,295). Data and bss are equal. The file grows by 8,192 bytes.Listings on the base and on this PR.
x -> .andy -> .Results: Bun 1.4.3 (367d939) against this branch. Each cell is
tests / filesfrom theRanline.x -> .andy -> .ulimit -n 64)examples/one/lib -> ../..andexamples/two/lib -> ../..a/loop -> ..andalias -> aa/loop -> ..sub/loop -> ..andsub/deeper/self -> .root = "tests"in bunfig.toml,tests/up -> ..a/loop -> ..with--isolateor--parallel=2shared -> ../outside(only route)pkg -> .store/pkg(only route)Filters and ignore patterns, tree
proj/shared -> ../outside, cwdproj.bun test shared/: 1.4.3 runs the test. This branch matches nothing and exits 1.bun test outside/: 1.4.3 matches nothing. This branch runs the test.bun test ./shared: both run the test.--path-ignore-patterns 'shared/**'and'shared': both prune the link.--path-ignore-patterns 'shared/*.test.js': 1.4.3 ignores the file. This branch runs it.--path-ignore-patterns '../outside/**': 1.4.3 runs the file. This branch ignores it.The 8 new tests that pass on 1.4.3 too. They pin what must not change: a test that only a link leads to still runs, a filter on the real path still matches, and a link that is the scan root still works. A walk that opens each directory with
O_NOFOLLOWcannot reach the directories of the first group.Why 1.4.0 ended. Before #40016 (in 1.4.1, not in 1.4.0) the walk held one fd for each directory, so it ended when no file descriptor was left.
What I ran. On a debug build of this head: the 25
directory linkstests and the 15 cases of #42921 pass, withpath-ignore-patterns.test.ts(10 pass),test-shard.test.ts(29 pass),pass-with-no-tests.test.ts(5 pass),test-changed.test.ts(20 pass) andtest/regression/issue/26851.test.ts(2 pass). The rest ofbun-test.test.tspasses, except that two cases of "nested too deeply to print" from #44353 take 5.5 s on my debug build and hit the 5 s limit, with and without this diff. CI ran the file on every lane, Windows included, where the rows use junctions.Known limits.
chmod 111): the walk skips the link as covered, and cannot list that directory by itself.get_fd_pathand the real path fromRealFS::kind. After it, the scanner's own lookup names every link target (open, fd path, close: 3 syscalls per directory link, 0 on a tree with no links), andreal_path_ofmoves tobun_sys::realpath.bust_dir_cache_recursive(src/runtime/api/filesystem_router.rs) andFrameworkRouter::scan_inner(src/runtime/bake/FrameworkRouter.rs).The reproductions come from #41465. That PR keeps only its other half, the report of a directory that cannot be read.